-- ======================================================================
-- Hell Logic Sniffer Addon
--
-- Copyright 2016 Hell-Prototypes
--
-- http://www.hellprototypes.com/
-- ======================================================================

ModuleClass = "LS_DECODER"
ModuleName  = "CUSTOM"
ModuleAlias = "MagCard Decoder"
ModuleParam1= "Track CH:"
ModuleTip   = [[
磁卡磁道解码，只转成位流，没有解析成数据

请选择磁道数据所在的通道，并使用[DEC]格式显示
]]

SKIP_LEADING_EDGE_NUM = 12 --skip 6 bit

ZERO_ERR_RATIO = 4 --容错系数，值越大越严格
ONE_ERR_RATIO  = 4

VALID_ZERO_BIT_THRESHOLD = 4 --连续有效前导零个数

--静态变量定义
TIME_IDX = 1
VALUE_IDX = 1
OFFSET_IDX = 2

g_error_count = 0

function get_edge_list()
	local g_ch_data = {}
	local g_ch_num = LS_PARAM[1]
	
	if g_ch_num < 0 then
		LS_API.MessageBox("<MagCard>Get current channel fail...")
		return nil
	end

	--get channel data
	g_ch_data = LS_API.GetChannelData(g_ch_num)
	if type(g_ch_data) ~= "table" then
    	LS_API.MessageBox("<MagCard>Read channel data fail...")
		return nil
    end

    if #g_ch_data < 64 then
    	LS_API.MessageBox("<MagCard>Invalid data in selected channel : " .. g_ch_num)
		return nil
    end
    --LS_API.DebugMsg("g_ch_data size = ".. #g_ch_data)
	
	local edge_list = {}
	local edge_list_idx = 1
	SKIP_LEADING_EDGE_NUM = SKIP_LEADING_EDGE_NUM * 2 + 1
	for idx=SKIP_LEADING_EDGE_NUM, #g_ch_data-SKIP_LEADING_EDGE_NUM, 2 do
		edge_list[edge_list_idx] = g_ch_data[idx]
		--LS_API.DebugMsg("@ " .. edge_list[edge_list_idx]*20)
		edge_list_idx = edge_list_idx + 1
	end

	return edge_list
end


function get_pulse_time_and_edge_list()
	local g_sampling_period = 0
	local common_param = LS_API.GetCommonParameter()
	--LS_API.DebugMsg("common_param len = ".. #common_param)
	if #common_param > 0 then
		g_sampling_period = common_param[1]
	end

	local edge_list = get_edge_list()
	if not edge_list then
		return nil
	end

	local pulse_time_and_edge_list = {}
	local list_idx = 1
	local last_edge = edge_list[1]
	for i=2, #edge_list do
		pulse_time_and_edge_list[list_idx] = {}
		pulse_time_and_edge_list[list_idx][TIME_IDX] = (edge_list[i] - last_edge)*g_sampling_period
		pulse_time_and_edge_list[list_idx][OFFSET_IDX] = edge_list[i-1]
		last_edge = edge_list[i]
		list_idx = list_idx + 1
	end

	return pulse_time_and_edge_list
end

--从位时间解成位数据
function magcard_bit_decode(pulse_time_and_edge_list)
	local last_last_bit_width = pulse_time_and_edge_list[1][TIME_IDX];
	local last_bit_width = pulse_time_and_edge_list[2][TIME_IDX];
	local average_bit_width
	local curr_bit_width = 0
	local idx_offset = 3
	
	--查找连续n个位宽相近的位做为前导零的起点
	local valid_zero_bit_cnt = 0
	for idx=idx_offset, idx_offset + VALID_ZERO_BIT_THRESHOLD * 8 do --使用前32个位宽来判断前导零区
		average_bit_width = (last_bit_width + last_last_bit_width)/2
		curr_bit_width = pulse_time_and_edge_list[idx][TIME_IDX];
		if math.abs(average_bit_width - curr_bit_width) < average_bit_width/ZERO_ERR_RATIO then
			valid_zero_bit_cnt = valid_zero_bit_cnt + 1
		else
			valid_zero_bit_cnt = 0
		end
		last_last_bit_width = last_bit_width
		last_bit_width = curr_bit_width
		if valid_zero_bit_cnt >= VALID_ZERO_BIT_THRESHOLD then --有连续8个位宽相似则判断为前导零区
			idx_offset = idx + 1
			break
		end
	end
	
	if valid_zero_bit_cnt < VALID_ZERO_BIT_THRESHOLD then
		LS_API.MessageBox("<MagCard>No leading zero data found")
		return nil
	end

	local bit_and_edge_list = {}
	local bit_and_edge_list_idx = 1;
	while idx_offset < (#pulse_time_and_edge_list - VALID_ZERO_BIT_THRESHOLD) do
		average_bit_width = (last_bit_width + last_last_bit_width)/2
		curr_bit_width = pulse_time_and_edge_list[idx_offset][TIME_IDX]
		while curr_bit_width < average_bit_width/ZERO_ERR_RATIO/4 do
			idx_offset = idx_offset + 1
			if idx_offset < #pulse_time_and_edge_list then
				curr_bit_width = curr_bit_width + pulse_time_and_edge_list[idx_offset][TIME_IDX]
			else
				return nil
			end
		end
		bit_and_edge_list[bit_and_edge_list_idx] = {}
		if math.abs(average_bit_width - curr_bit_width) < average_bit_width/ZERO_ERR_RATIO then
			--位宽时间和平均值相似判为0
			bit_and_edge_list[bit_and_edge_list_idx][VALUE_IDX] = 0
			bit_and_edge_list[bit_and_edge_list_idx][OFFSET_IDX] = pulse_time_and_edge_list[idx_offset][OFFSET_IDX]
		else
			local average_half_bit_width = average_bit_width/2
			local next_bit_width = pulse_time_and_edge_list[idx_offset+1][TIME_IDX]
			local exceptional_bit = 1 --干扰脉冲
			while next_bit_width < average_bit_width/ZERO_ERR_RATIO/4 do
				exceptional_bit = exceptional_bit + 1
				if idx_offset < #pulse_time_and_edge_list then
					next_bit_width = next_bit_width + pulse_time_and_edge_list[idx_offset + exceptional_bit][TIME_IDX]
				else
					return nil
				end
			end
			if math.abs(average_bit_width - next_bit_width - curr_bit_width) < average_bit_width/ONE_ERR_RATIO then
				--两个位宽时间加起来和平均值相似判为1
				bit_and_edge_list[bit_and_edge_list_idx][OFFSET_IDX] = pulse_time_and_edge_list[idx_offset][OFFSET_IDX]
				if math.abs(average_half_bit_width - curr_bit_width) > average_half_bit_width/ONE_ERR_RATIO then
					bit_and_edge_list[bit_and_edge_list_idx][VALUE_IDX] = "1?"
					g_error_count = g_error_count + 1
					LS_API.MoveLeftCursorTo(bit_and_edge_list[bit_and_edge_list_idx][OFFSET_IDX])
				else
					bit_and_edge_list[bit_and_edge_list_idx][VALUE_IDX] = 1
				end
				curr_bit_width = curr_bit_width + next_bit_width 
				idx_offset = idx_offset + exceptional_bit
			else
				bit_and_edge_list[bit_and_edge_list_idx][VALUE_IDX] = "ERR"
				g_error_count = g_error_count + 1
				bit_and_edge_list[bit_and_edge_list_idx][OFFSET_IDX] = pulse_time_and_edge_list[idx_offset][OFFSET_IDX]
				LS_API.MoveRightCursorTo(bit_and_edge_list[bit_and_edge_list_idx][OFFSET_IDX])
			end
		end
		
		bit_and_edge_list_idx = bit_and_edge_list_idx + 1

		last_last_bit_width = last_bit_width
		last_bit_width = curr_bit_width
		idx_offset = idx_offset + 1
	end

	return bit_and_edge_list
end

function main ()
	local pulse_time_and_edge_list = get_pulse_time_and_edge_list()
	if not pulse_time_and_edge_list then
		return
	end

	g_error_count = 0

	local bit_and_edge_list = magcard_bit_decode(pulse_time_and_edge_list)
	
	if not bit_and_edge_list then
		return
	end

	decode_out = {}
	for idx=1, #bit_and_edge_list do
		decode_out[1] = bit_and_edge_list[idx][OFFSET_IDX]
		decode_out[2] = bit_and_edge_list[idx][VALUE_IDX]
		decode_out[3] = bit_and_edge_list[idx][OFFSET_IDX]
		LS_API.DecodeElementPushBack(decode_out)
	end
	if g_error_count > 0 then
		LS_API.MessageBox("<MagCard> Detected [ " .. g_error_count .. " ] Error bits, please check ..")
	end
end
